/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ems.controller;

import ems.database.DBConnection;
import ems.database.ResourceDAO;
import ems.database.SessionDAO;
import ems.database.StaffDAO;
import ems.database.TaskDAO;
import ems.lib.Resource;
import ems.lib.Staff;
import ems.lib.Task;
import ems.ui.TextUI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 *
 * @author hoa
 */
public class Engine {

    private static SessionDAO sessManager = new SessionDAO();
    private static TaskDAO taskManager = new TaskDAO();
    private static ResourceDAO resManager = new ResourceDAO();
    private static StaffDAO staffManager = new StaffDAO();
    private static Staff staff = new Staff();
    private static List<Resource> lres = new ArrayList<Resource>();
    private static List<Task> ltask = new ArrayList<Task>();
    private static List<Staff> lstaff = new ArrayList<Staff>();
    private static List<String> message = new ArrayList<String>();
    private static String cmd = "";


    /**
     * Log in a staff based on the input credential
     * @param username
     * @param password
     * @return staff object
     */
    public static Staff login(String username, String password) {
        staff = sessManager.login(username, password);
        if (staff == null) {
            TextUI.displayLoginFail();
        } else {
            TextUI.displayConsole(staff);
        }
        return staff;
    }

    /**
     * Log in a staff based on the input credential
     * @param username
     * @param password
     * @return staff object
     */
    public static Staff login(String username, char[] password) {
        StringBuffer result = new StringBuffer();
        if (password.length > 0) {
            result.append(password[0]);
            for (int i = 1; i < password.length; i++) {
                result.append("");
                result.append(password[i]);
            }
        }

        staff = sessManager.login(username, password);
        if (staff == null) {
            TextUI.displayLoginFail();
        } else {
            TextUI.displayConsole(staff);
        }
        return staff;
    }

    /**
     * Execute a command from user input, depending on user's role
     * @param ss
     */
    public static void execute(String ss) {
        String argz = "";
        if (ss.trim().isEmpty()) {
            cmd = "";
        } else {
            String[] ars = ss.trim().split(" ");
            cmd = ars[0];
            argz = (ars.length > 1) ? ars[1] : "";
        }

        // Field Officer command list
        if (staff.getJob().equals("Field Officer")) {
            if (cmd.equalsIgnoreCase("view")) {
                Task t = taskManager.getTaskByStaff(staff);
                HashMap hm = new HashMap();
                if (t != null) {
                    hm = taskManager.getResourceAllocated(t.getTaskId());
                }
                TextUI.displayFOAssignedTask(t, hm);
                //, taskManager.getResourceAllocated(id)
            } else if (cmd.equalsIgnoreCase("report")) {

                Task t = taskManager.getTaskByStaff(staff);
                if (t == null) {
                    TextUI.promptLn("---   You have no task assigned.   ---");
                } else {
                    TextUI.promptLn("Current task ID: " + t.getTaskId());
                    TextUI.promptLn("Report Completed? (y/n):");
                    boolean answer = TextUI.getDecision();
                    if (answer) {
                        taskManager.closeTask(t);
                        TextUI.promptLn("Task has been closed");
                        System.out.println();
                    }
                }

            } else if (cmd.equalsIgnoreCase("help")) {
                TextUI.displayFOHelp();
            } else if (cmd.equalsIgnoreCase("request")) {
                Task t = taskManager.getTaskByStaff(staff);
                if (t == null) {
                    TextUI.promptLn("---   You have no task assigned.   ---");
                } else {
                    TextUI.promptLn("--- Unavailable Function At the Moment ---");
                    /*
                    TextUI.prompt(" | You wish to request additional resources for current task? (y/n): ");
                    boolean answer = TextUI.getDecision();
                    if (answer) {
                        taskManager.updateResourceRequired(t.getTaskId(), TextUI.requestResources());
                    }*/
                }


            } else {
                executeCommonCmd(cmd);
            }
        }

        // Call Taker command list
        if (staff.getJob().equals("Call Taker")) {
            if (cmd.equalsIgnoreCase("create")) {
                TextUI.displayCreateTask();
            } else if (cmd.equalsIgnoreCase("help")) {
                TextUI.displayCTHelp();
            } else if (cmd.equalsIgnoreCase("taskmap")) {
                TextUI.displayTaskMap(taskManager.getAllTask(""));
            } else {
                executeCommonCmd(cmd);
            }
        }

        // Dispatcher command list
        if (staff.getJob().equals("Dispatcher")) {
            if (cmd.equalsIgnoreCase("view")) {
                if (argz.equals("")) {
                    TextUI.displayTaskMap(taskManager.getAllTask(""));
                } else if (checkInt(argz)) {
                    ltask = taskManager.getTask(Integer.parseInt(argz));
                    if (ltask.isEmpty()) {
                        invalidCommand("Invalid taskid", "WARNING");
                    } else {
                        TextUI.displayTaskListDetail(ltask, 
                                taskManager.getResourceRequired(Integer.parseInt(argz)),
                                taskManager.getResourceAllocated(Integer.parseInt(argz)),
                                taskManager.getStaffByTask(Integer.parseInt(argz)));
                    }
                } else {
                    invalidCommand("Invalid taskid", "WARNING");
                }
            } else if (cmd.equalsIgnoreCase("help")) {
                TextUI.displayDPHelp();
            } else if (cmd.equalsIgnoreCase("taskmap")) {
                if (argz.equals("")) {
                    TextUI.displayTaskMap(taskManager.getAllTask(""));
                } else if ( argz.equalsIgnoreCase("undispatched")
                        || argz.equalsIgnoreCase("dispatched")
                        || argz.equalsIgnoreCase("closed")) {
                    TextUI.displayTaskMap(taskManager.getAllTask(argz));
                } else {
                    TextUI.displayTaskMap(taskManager.getAllTask(""));
                }
            } else if (cmd.equalsIgnoreCase("resourcemap")) {
                lres = resManager.getAllResource(argz);
                lstaff = staffManager.getAllFO(argz);
                TextUI.displayResourceMap(lstaff, lres);
            } else if (cmd.equalsIgnoreCase("dispatch")) {
                if (argz.equals("") || !checkInt(argz)) {
                    message.add("Usage: dispatch <taskid>");
                    TextUI.displayMessage(message, "WARNING");
                    message.clear();
                } else {
                    int errCnt = 0;

                    while (errCnt == 0) {
                        List<Task> lt = taskManager.getTask(Integer.parseInt(argz));
                        if (lt.isEmpty()) {
                            invalidCommand("Task ID is invalid.","ERROR");
                            errCnt++;
                            break;
                        } else {
                            if (lt.get(0).getStatus().equals("DISPATCHED")) {
                                invalidCommand("This task has already been dispatched.","ERROR");
                                errCnt++;
                                break;
                            } else if (lt.get(0).getStatus().equals("CLOSED")) {
                                invalidCommand("This task has already been closed.","ERROR");
                                errCnt++;
                                break;
                            }
                        }

                        // need to check for available resource first
                        lstaff = staffManager.getAllFO("AVAILABLE");
                        if (lstaff.isEmpty()) {
                            TextUI.promptLn("WARNING: Can't dispatch task. Not enough available Field Officer.");
                            errCnt++;
                        }

                        HashMap hm = taskManager.getResourceRequired(Integer.parseInt(argz));
                        if (!isEnoughResource(hm)) {
                            TextUI.promptLn("WARNING: Can't dispatch task. Not enough available resources.");
                            errCnt++;
                        }

                        if (errCnt < 1) {
                            ltask = taskManager.getTask(Integer.parseInt(argz));
                            lres = resManager.getAllResource("AVAILABLE");
                            TextUI.displayDispatchTask(ltask, hm, lres, lstaff);
                            break;
                        } else {
                            break;
                        }
                    }
                }
            } else if (cmd.equalsIgnoreCase("autodispatch")) {
                if (argz.equals("") || !checkInt(argz)) {
                    message.add("Usage: autodispatch <taskid>");
                    TextUI.displayMessage(message, "WARNING");
                    message.clear();
                } else {
                    int errCnt = 0;

                    while (errCnt == 0) {
                        List<Task> lt = taskManager.getTask(Integer.parseInt(argz));
                        if (lt.isEmpty()) {
                            invalidCommand("Task ID is invalid.","ERROR");
                            errCnt++;
                            break;
                        } else {
                            if (lt.get(0).getStatus().equals("DISPATCHED")) {
                                invalidCommand("This task has already been dispatched.","ERROR");
                                errCnt++;
                                break;
                            } else if (lt.get(0).getStatus().equals("CLOSED")) {
                                invalidCommand("This task has already been closed.","ERROR");
                                errCnt++;
                                break;
                            }
                        }

                        // need to check for available resource first
                        lstaff = staffManager.getAllFO("AVAILABLE");
                        if (lstaff.isEmpty()) {
                            TextUI.promptLn("WARNING: Can't dispatch task. Not enough available Field Officer.");
                            errCnt++;
                        }

                        HashMap hm = taskManager.getResourceRequired(Integer.parseInt(argz));
                        if (!isEnoughResource(hm)) {
                            TextUI.promptLn("WARNING: Can't dispatch task. Not enough available resources.");
                            errCnt++;
                        }

                        if (errCnt < 1) {
                           TextUI.promptLn(taskManager.autoDispatchTask(Integer.parseInt(argz)));
                           ltask = taskManager.getTask(Integer.parseInt(argz));
                           TextUI.displayTaskListDetail(ltask,
                                taskManager.getResourceRequired(Integer.parseInt(argz)),
                                taskManager.getResourceAllocated(Integer.parseInt(argz)),
                                taskManager.getStaffByTask(Integer.parseInt(argz)));
                            break;
                        } else {
                            break;
                        }
                    }
                }
            } else if (cmd.equalsIgnoreCase("cancel")) {
                System.out.println("");
            } else {
                executeCommonCmd(cmd);
            }
        }

    }

    /**
     * Execution of commands common across all user role
     * @param cmd
     */
    public static void executeCommonCmd(String cmd) {
        // Staff generic cmd
        if (cmd.equalsIgnoreCase("logout")) {
            logout();
        } else if (cmd.equalsIgnoreCase("quit")) {
            shutdown();
        } else if (cmd.equals("")) {
        } else {
            invalidCommand("Invalid Command.", "WARNING");
            invalidCommand("Type 'help' to see the manual screen.", "USAGE");
        }
    }

    /**
     * Utility to check for whether there is enough resources to dispatch
     * @param hm
     * @return true or false
     */
    public static boolean isEnoughResource(HashMap hm) {
        boolean isEnough = true;
        String resType = "";
        Iterator iter = hm.entrySet().iterator();
        while (iter.hasNext()) {
            Map.Entry mEntry = (Map.Entry) iter.next();
            resType = mEntry.getKey().toString();
            // check list size against res required
            if (resManager.getResourceWithType("AVAILABLE", resType).size() < Integer.parseInt(mEntry.getValue().toString())) {
                isEnough = false;
                break;
            }
        }
        return isEnough;
    }

    /**
     * Process dispatching task
     * @param taskID
     * @param staffID
     * @param lresid
     * @return true of false
     */
    public static boolean processDispatchTask(int taskID, int staffID, List<Integer> lresid) {
        try {
            TextUI.promptLn("Processing...");
            taskManager.dispatchTask(taskID, staffID, lresid);
            TextUI.promptLn("Successfuly dispatched task!");
            return true;
        } catch (Exception e) {
            invalidCommand("An error occurred with the database.", "ERROR");
            return false;
        }
    }

    /**
     * Sent data to TextUI to display resource map
     * @param filter
     */
    public static void displayResourceMap(String filter) {
        lres = resManager.getAllResource(filter);
        lstaff = staffManager.getAllFO(filter);
        TextUI.displayResourceMap(lstaff, lres);
    }

    /**
     * Process the data from user input to create task
     * @param ls
     */
    public static void processCreateTask(List ls) {
        Task t = new Task(ls.get(0).toString(), ls.get(1).toString(),
                ls.get(2).toString(), Integer.parseInt(ls.get(3).toString()), "UNDISPATCHED");
        int tid = taskManager.addTask(t);

        for (int i = 0; i < 4; i++) {
            ls.remove(0);
        }

        if (tid > 0) {
            taskManager.setResourceRequired(tid, ls);
        }
    }

    /**
     * Utility to check whether a string is a valid positive integer
     * @param s
     * @return true or false
     */
    public static boolean checkInt(String s) {
        boolean isInteger = false;
        try {
            int x = Integer.parseInt(s);
            if (x >= 0) {
                isInteger = true;
            }
        } catch (NumberFormatException nFE) {
        }
        return isInteger;
    }

    /**
     * Utility to check whether a string is a valid positive integer and within a range
     * @param s
     * @param min
     * @param max
     * @return true or false
     */
    public static boolean checkInt(String s, int min, int max) {
        boolean isInteger = false;
        try {
            int x = Integer.parseInt(s);
            if (x >= min && x <= max) {
                isInteger = true;
            }
        } catch (NumberFormatException nFE) {
        }
        return isInteger;
    }

    /**
     * @deprecated
     * Test function
     * @return
     */
    public static int banana() {
        System.out.println("BANANANANAN!");
        return 1;
    }

    /**
     * Show the invalid command in textUI
     * @param msg
     * @param type
     */
    public static void invalidCommand(String msg, String type) {
        msg = (msg.isEmpty()) ? "Invalid command." : msg;
        type = (type.isEmpty()) ? "WARNING" : type;
        message.add(msg);
        TextUI.displayMessage(message, type);
        message.clear();
    }

    /**
     * Log out the user and return to login screen
     */
    public static void logout() {
        staff = null;
        ltask.clear();
        TextUI.displayLoginScreen();
    }

    /**
     * Shutdown EMS and exit
     */
    public static void shutdown() {
        DBConnection.disconnect();
        System.exit(0);
    }

    /**
     * Main method
     *
     */
    public static void main(String[] args) {
        try {
            //DBConnection.connect();

            TextUI.clearScreen();
            TextUI.displayWelcomeScreen();
            TextUI.displayLoginScreen();

            //do {
            //    System.out.println();
            //}  while (true);

        } catch (Exception e) {
            System.err.println(e);

        }
    }
}
